/**
 * @author: Loy 
 * @date: 2014-6-27-下午3:11:12
 */
package net.loyintean.pearlshell.base.service.impl;

import org.apache.log4j.Logger;

import net.loyintean.pearlshell.base.model.ICachable;
import net.loyintean.pearlshell.base.model.ILogInfo;
import net.loyintean.pearlshell.base.model.impl.LogInfoFactory;
import net.loyintean.pearlshell.base.service.ICacheService;
import net.loyintean.pearlshell.base.service.ISeekHandler;
import net.loyintean.pearlshell.base.service.ISeekService;

/**
 * a template to seek model.
 * <p>
 * Loy: fix a NullPointerException. v20140902.1.0.1
 * 
 * @author Loy
 * @version 20140902.1.0.1
 */
public class SeekTemplateServImpl<T extends ICachable> implements
		ISeekService<T> {

	/**
	 * logger
	 * 
	 */
	private final Logger logger = Logger.getLogger(this.getClass());

	/**
	 * read data from cache
	 * 
	 */
	private ICacheService<T> cache;

	/**
	 * seek data from somewhere but not cache.
	 * 
	 */
	private ISeekService<T> orginSeeker;

	/**
	 * handle data after read it
	 * 
	 */
	private ISeekHandler<T> handler;

	/**
	 * 
	 * @author Loy
	 * @see com.sinosig.ifinace.microportal.base.seek.service.IseekService
	 *      #seek(java.lang.Object)
	 * 
	 */
	@Override
	public T seek(T param) {
		assert param != null;

		ILogInfo logInfo = LogInfoFactory.BUILD("@param=" + param);

		logger.info(logInfo.start());

		T result = null;

		if (cache != null) {
			// read data from cache firstly
			result = seekFromCache(param, logInfo);
		}

		// if nothing in cache
		// then seek data from it's orgin source.
		if (result == null) {
			result = seekFromOrgin(param, logInfo);
		}

		if (handler != null) {
			// if a handler was configurated, then deal it.
			result = handleAfterseek(param, result, logInfo);
		}

		// write the result into cache.
		// 2014-9-2 林俊 start
		// avoid NullPointerException from cache.
		if (cache != null && result != null) {
			cache.wrtie(result);
		}
		// 2014-9-2 林俊 end

		logger.info(logInfo.end("result=" + result));

		return result;
	}

	/**
	 * read data from cache.
	 * <p>
	 * depends on {@link #cache}
	 * 
	 * @param param
	 *            param for seek data. depends on
	 *            {@link ICachableModel#getKey()}.
	 * @param logInfo
	 *            info for logger
	 * @return the data in cache.
	 */
	private T seekFromCache(T param, ILogInfo logInfo) {

		assert param != null;
		assert logInfo != null;
		assert cache != null;

		// read data from cache firstly
		T result = cache.read(param.getKey());

		if (logger.isDebugEnabled()) {
			logger.debug(logInfo.inner("result in cache =" + result));
		}

		return result;
	}

	/**
	 * read data from orgin resource, and write back into cache.
	 * <p>
	 * depends on {@link #seeker}.
	 * 
	 * @param param
	 *            param for seek data.
	 * @param logInfo
	 *            info for logger
	 * @return the data in orgin resource.
	 */
	private T seekFromOrgin(T param, ILogInfo logInfo) {
		assert param != null;
		assert logInfo != null;
		assert orginSeeker != null;

		T result = orginSeeker.seek(param);

		if (logger.isDebugEnabled()) {
			logger.debug(logInfo.inner("result from seeker =" + result));
		}
		return result;
	}

	/**
	 * deal something after seek if necessary.
	 * 
	 * @param param
	 *            the data used to seek the result.
	 * @param result
	 *            the result found through param.
	 * @param logInfo
	 *            info for logger
	 * @return result itself while handler is null; or another result be
	 *         handled.
	 */
	private T handleAfterseek(T param, T result, ILogInfo logInfo) {
		assert logInfo != null;

		T after = result;

		if (result != null) {
			after = handler.handle(param, result);
		}

		if (logger.isDebugEnabled()) {
			logger.debug(logInfo.inner("result after handle =" + result));
		}

		return after;
	}

	/**
	 * @param cache
	 *            the {@link #cache }to set
	 */
	public void setCache(ICacheService<T> cache) {
		this.cache = cache;
	}

	/**
	 * @param handler
	 *            the {@link #handler }to set
	 */
	public void setHandler(ISeekHandler<T> handler) {
		this.handler = handler;
	}

	/**
	 * @param orginSeeker
	 *            the {@link #orginSeeker }to set
	 */
	public void setOrginSeeker(ISeekService<T> orginSeeker) {
		this.orginSeeker = orginSeeker;
	}

}
